#ifndef EFFECT_PARAMS_H
#define EFFECT_PARAMS_H

#include "Vector2.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Matrix.h"
#include "BaseTexture.h"
#include "CommonTypes.h"

namespace Cvekas {

class Effect;

/// Provides easy way to set parameters used by Effect
class EffectParams : public boost::noncopyable
{
public:
	/// Constructor. 
	/// Rule of using smart pointers everywhere is broken here, because Effect
	/// needs to construct EffectParams instance in its constructor.
	EffectParams(Effect* owner);
	~EffectParams();

	void add(const std::string& name, float value);
	void add(const std::string& name, int value);
	void add(const std::string& name, bool value);
	void add(const std::string& name, const Vector2& value);
	void add(const std::string& name, const Vector3& value);
	void add(const std::string& name, const Vector4& value);
	void add(const std::string& name, const Matrix& value);
	void add(const std::string& name, TexturePtr value);

	void add(ParamHandle handle, float value);
	void add(ParamHandle handle, int value);
	void add(ParamHandle handle, bool value);
	void add(ParamHandle handle, const Vector2& value);
	void add(ParamHandle handle, const Vector3& value);
	void add(ParamHandle handle, const Vector4& value);
	void add(ParamHandle handle, const Matrix& value);
	void add(ParamHandle handle, TexturePtr value);

	void addDynamic(const std::string& name, const float* value);
	void addDynamic(const std::string& name, const int* value);
	void addDynamic(const std::string& name, const bool* value);
	void addDynamic(const std::string& name, const Vector2* value);
	void addDynamic(const std::string& name, const Vector3* value);
	void addDynamic(const std::string& name, const Vector4* value);
	void addDynamic(const std::string& name, const Matrix* value);
	void addDynamic(const std::string& name, const TexturePtr* value);

	void addDynamic(ParamHandle handle, const float* value);
	void addDynamic(ParamHandle handle, const int* value);
	void addDynamic(ParamHandle handle, const bool* value);
	void addDynamic(ParamHandle handle, const Vector2* value);
	void addDynamic(ParamHandle handle, const Vector3* value);
	void addDynamic(ParamHandle handle, const Vector4* value);
	void addDynamic(ParamHandle handle, const Matrix* value);
	void addDynamic(ParamHandle handle, const TexturePtr* value);

	void applyParams();

private:
	
	union DWordValue
	{
		float float_val;
		int int_val;
		bool bool_val;
	};

	struct PtrValue
	{
		const void* ptr;
		uint size;
	};

	template <typename T>
	struct Param
	{
		ParamHandle handle;
		T value;
	};

	std::vector<Param<DWordValue> > dword_params;
	std::vector<Param<Vector2> > vector2_params;
	std::vector<Param<Vector3> > vector3_params;
	std::vector<Param<Vector4> > vector4_params;
	std::vector<Param<Matrix> > matrix_params;
	std::vector<Param<TexturePtr> > texture_params;
	std::vector<Param<PtrValue> > ptr_params;
	std::vector<Param<const TexturePtr*> > texture_ptr_params;

	Effect* owner;

	void checkParam(ParamHandle param);
	
	template <typename T>
	Param<T> constructParam(ParamHandle handle)
	{
		checkParam(handle);
		assert(handle);

		Param<T> result;
		result.handle = handle;

		return result;
	}

	template <typename T>
	Param<T> constructParam(const std::string& name)
	{
		ParamHandle handle = owner->getParamByName(name);
		assert(handle);
		checkParam(handle);
		
		Param<T> result;
		result.handle = handle;

		return result;
	}
};

typedef boost::shared_ptr<EffectParams> EffectParamsPtr;

} // namespace

#endif